//
//  FCSRCategoryPropertyDefines.h
//  SBCProject
//
//  Created by sobey on 2020/5/14.
//  Copyright © 2020 ZhouYou. All rights reserved.
//

#ifndef FCSRCategoryPropertyDefines_h
#define FCSRCategoryPropertyDefines_h

@import ObjectiveC;


/**
 Synthsize a dynamic c type property in @implementation scope.
 It allows us to add custom properties to existing classes in categories.

 @warning @import ObjectiveC;
 *******************************************************************************
 Example:
 @interface NSObject (MyAdd)
 @property (nonatomic, retain) CGPoint myPoint;
 @end

 @import ObjectiveC;
 @implementation NSObject (MyAdd)
 T_DYNAMIC_PROPERTY_CTYPE(myPoint, setMyPoint, CGPoint)
 @end
 */
#ifndef T_DYNAMIC_PROPERTY_CTYPE
#define T_DYNAMIC_PROPERTY_CTYPE(_getter_, _setter_, _type_)                 \
  -(void)_setter_ : (_type_)object                                           \
  {                                                                          \
    NSValue *value = [NSValue value:&object withObjCType:@encode(_type_)];   \
    objc_setAssociatedObject(self, _cmd, value, OBJC_ASSOCIATION_RETAIN);    \
  }                                                                          \
  -(_type_)_getter_                                                          \
  {                                                                          \
    _Pragma("clang diagnostic push")                                         \
      _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"")   \
        _type_ cValue = {0};                                                 \
    _Pragma("clang diagnostic pop")                                          \
      NSValue *value = objc_getAssociatedObject(self, @selector(_setter_:)); \
    [value getValue:&cValue];                                                \
    return cValue;                                                           \
  }

#endif


/**
 Synthsize a dynamic object property in @implementation scope.
 It allows us to add custom properties to existing classes in categories.

 @warning @import ObjectiveC;
 *******************************************************************************
 Example:
     @interface NSObject (MyAdd)
     @property (nonatomic) UIColor *myColor;
     @end

     @import ObjectiveC;
     @implementation NSObject (MyAdd)
     T_DYNAMIC_PROPERTY_OBJECT(myColor, setMyColor)
     @end
 */

#ifndef T_DYNAMIC_PROPERTY_OBJECT
#define T_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_)                                \
  _Pragma("clang diagnostic push")                                                   \
      _Pragma("clang diagnostic ignored \"-Wmismatched-parameter-types\"")           \
        _Pragma("clang diagnostic ignored \"-Wmismatched-return-types\"") -          \
    (void)_setter_ : (id)object                                                      \
  {                                                                                  \
    objc_setAssociatedObject(self, _cmd, object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
  }                                                                                  \
  -(id)_getter_                                                                      \
  {                                                                                  \
    return objc_getAssociatedObject(self, @selector(_setter_:));                     \
  }                                                                                  \
  _Pragma("clang diagnostic pop")

#endif

#ifndef T_DYNAMIC_PROPERTY_WEAK_OBJECT
#define T_DYNAMIC_PROPERTY_WEAK_OBJECT(_getter_, _setter_)                                                                     \
  _Pragma("clang diagnostic push")                                                                                             \
      _Pragma("clang diagnostic ignored \"-Wmethod-signatures\"") -                                                            \
    (void)_setter_ : (id)object                                                                                                \
  {                                                                                                                            \
    TWeakObject *wObj = [TWeakObject objectWithBlock:^{                                                                        \
      objc_setAssociatedObject(self, @selector(_setter_:), nil, OBJC_ASSOCIATION_ASSIGN);                                      \
    }];                                                                                                                        \
    objc_setAssociatedObject(object, (__bridge const void *_Nonnull)(wObj.refBlock), wObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
    objc_setAssociatedObject(self, _cmd, object, OBJC_ASSOCIATION_ASSIGN);                                                     \
  }                                                                                                                            \
  -(id)_getter_                                                                                                                \
  {                                                                                                                            \
    return objc_getAssociatedObject(self, @selector(_setter_:));                                                               \
  }                                                                                                                            \
  _Pragma("clang diagnostic pop")
#endif

#ifndef T_DYNAMIC_PROPERTY_BOOL
#define T_DYNAMIC_PROPERTY_BOOL(_getter_, _setter_)                                     \
  -(void)_setter_ : (BOOL)object                                                        \
  {                                                                                     \
    objc_setAssociatedObject(self, _cmd, @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
  }                                                                                     \
  -(BOOL)_getter_                                                                       \
  {                                                                                     \
    return [objc_getAssociatedObject(self, @selector(_setter_:)) boolValue];            \
  }

#endif


#ifndef T_DYNAMIC_PROPERTY_INT
#define T_DYNAMIC_PROPERTY_INT(_getter_, _setter_)                                      \
  _Pragma("clang diagnostic push")                                                      \
      _Pragma("clang diagnostic ignored \"-Wmismatched-parameter-types\"")              \
        _Pragma("clang diagnostic ignored \"-Wmismatched-return-types\"") -             \
    (void)_setter_ : (NSInteger)object                                                  \
  {                                                                                     \
    objc_setAssociatedObject(self, _cmd, @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
  }                                                                                     \
  -(NSInteger)_getter_                                                                  \
  {                                                                                     \
    return [objc_getAssociatedObject(self, @selector(_setter_:)) integerValue];         \
  }                                                                                     \
  _Pragma("clang diagnostic pop")
#endif

#ifndef T_DYNAMIC_PROPERTY_UINT
#define T_DYNAMIC_PROPERTY_UINT(_getter_, _setter_)                                     \
  _Pragma("clang diagnostic push")                                                      \
      _Pragma("clang diagnostic ignored \"-Wmismatched-parameter-types\"")              \
        _Pragma("clang diagnostic ignored \"-Wmismatched-return-types\"") -             \
    (void)_setter_ : (NSUInteger)object                                                 \
  {                                                                                     \
    objc_setAssociatedObject(self, _cmd, @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
  }                                                                                     \
  -(NSUInteger)_getter_                                                                 \
  {                                                                                     \
    return [objc_getAssociatedObject(self, @selector(_setter_:)) unsignedIntegerValue]; \
  }                                                                                     \
  _Pragma("clang diagnostic pop")
#endif


#ifndef T_DYNAMIC_PROPERTY_CGFLOAT
#if CGFLOAT_IS_DOUBLE == 1
#define T_DYNAMIC_PROPERTY_CGFLOAT T_DYNAMIC_PROPERTY_DOUBLE
#else
#define T_DYNAMIC_PROPERTY_CGFLOAT T_DYNAMIC_PROPERTY_FLOAT
#endif
#endif

#ifndef T_DYNAMIC_PROPERTY_FLOAT
#define T_DYNAMIC_PROPERTY_FLOAT(_getter_, _setter_)                                    \
  -(void)_setter_ : (float)object                                                       \
  {                                                                                     \
    objc_setAssociatedObject(self, _cmd, @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
  }                                                                                     \
  -(float)_getter_                                                                      \
  {                                                                                     \
    return [objc_getAssociatedObject(self, @selector(_setter_:)) floatValue];           \
  }

#endif

#ifndef T_DYNAMIC_PROPERTY_DOUBLE
#define T_DYNAMIC_PROPERTY_DOUBLE(_getter_, _setter_)                                   \
  -(void)_setter_ : (double)object                                                      \
  {                                                                                     \
    objc_setAssociatedObject(self, _cmd, @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
  }                                                                                     \
  -(double)_getter_                                                                     \
  {                                                                                     \
    return [objc_getAssociatedObject(self, @selector(_setter_:)) doubleValue];          \
  }

#endif

#endif /* FCSRCategoryPropertyDefines_h */
